<?php

namespace App\Recharge\Service;

use Throwable;
use App\Recharge\Library;
use Psr\Log\LoggerInterface;
use App\Models\Mysql\CycleOrder;
use Illuminate\Support\Facades\Log;
use App\Models\Mysql\XnSupplierGoods;
use App\Recharge\Bean\BasicInformation;
use App\Recharge\Bean\CyclePunchChildBean;
use App\Models\Admin\InterfaceConfiguration;
use App\Models\Mysql\XnSupplierGoodsRelation;
use App\Recharge\Exceptions\SuspensionOfEnforcement;
use App\Models\Mysql\XnSupplierGoodsMerchantRelation;
use App\Recharge\Exceptions\CyclePunch\CyclePunchException;
use App\Recharge\Exceptions\CyclePunch\OrderFailedException;

/**
 * 循环冲服务层
 */
class CycleService extends BaseService
{

    /**
     * 执行操作
     *
     * @return void
     */
    public function execute(): void
    {
        try {
            // 主商品
            $this->mainGood();
            // 便利正常的
            if (!$this->each($this->getChildProduct()) && !$this->each($this->getPeakFailureChildren())){
                throw new OrderFailedException();
            }
        } catch (Throwable $e) {
            if ($e instanceof CyclePunchException && $e->isOrderFailed()) {
                $this->allOrdersFailed($e->getMessage());
            }
            $this->log()->error('循环冲消费操作失败:' . $e->getMessage(), [
                'data'      => $this->bean->toArray(),
                'order'     => $this->order->toArray(),
                'supplier'  => $this->supplier->toArray(),
                'trace'     => error2array($e),
            ]);
        }
    }

    /**
     * 获取子商品
     *
     * @return array
     */
    protected function getChildProduct(): array
    {
        $children = [];
        // 主商品
        $good = $this->bean->getGood();
        // 获取关联信息
        $relations = $good->xunhuanRelation($this->order)->orderByDesc('percentage')->get();
        // 判断是否为空
        if ($relations->isEmpty()) {
            goto end;
        }
        $this->bean->setRelations($relations);
        // 获取已经充值过的订单
        $order = $good->eachOrderChildren($this->order->order_sn)->get()->toArray();
        // 获取已经下过单的商品id
        $gaveUpGids = array_values(array_unique(array_column($order, 'gid')));
        // 过滤前子商品
        $beforeFilteringChildren = [];
        // 排序
        $sorts = $unitPrices = $percentages = [];
        // 便利关系
        foreach ($relations as $key => $relation) {
            /**
             * @var XnSupplierGoodsRelation|XnSupplierGoodsMerchantRelation $relation
             * @var XnSupplierGoods|null $child
             */
            $child = $relation->good()->first();
            if (!$child) {
                continue;
            }
            $child = $child->toArray();
            // 排序
            $child['r_sort'] = $relation->sort;
            // 百分比
            $child['r_percentage'] = $relation->percentage;
            // 添加到未过滤的
            $beforeFilteringChildren[] = $child;
            // 判断是否在里面
            if (in_array($child['id'], $gaveUpGids)) {
                continue;
            }
            $children[$key] = $child;
            // 排序
            $sorts[$key] = $relation->sort;
            $unitPrices[$key] = $child['unit_price'];
            $percentages[$key] = $relation->percentage;
        }
        // 过滤前子商品
        $this->bean->setChildren($beforeFilteringChildren);
        if (!empty($children)) {
            // 排序
            array_multisort($percentages, SORT_DESC, $unitPrices, SORT_ASC, $sorts, SORT_DESC, $children);
        }
        // 返回
        end: return $children;
    }

    /**
     * 获取因为峰值失败的子商品
     *
     * @return array
     */
    protected function getPeakFailureChildren(): array
    {
        // 主商品
        $good = $this->bean->getGood();
        // 获取已经充值过的订单
        $orders = $this->logic->peakFailureCycleOrder($good, $this->order->order_sn);
        if ($orders->isEmpty()) {
            return [];
        }
        $gids = [];
        // 过滤已经执行过一次的
        foreach ($orders as $order) {
            /* @var CycleOrder $order */
            if ($this->logic->countSimilarOrders($order) > 0) {
                continue;
            }
            $gids[] = $order->gid;
        }
        // 判断子商品
        $children = [];
        foreach ($this->bean->getChildren() as $child) {
            if (in_array($child['id'], $gids)) {
                $children[] = $child;
            }
        }
        if (empty($children)) {
            return [];
        }
        // 排序
        $sorts = $unitPrices = [];
        foreach ($children as $key => $child) {
            // 排序
            $sorts[$key] = $child['r_sort'];
            $unitPrices[$key] = $child['unitPrice'];
            $children[$key]['peak_failure'] = 1;
        }
        // 排序
        array_multisort($unitPrices, SORT_ASC, $sorts, SORT_DESC, $children);
        // 返回
        return $children;
    }

    /**
     * 便利子商品
     *
     * @param array $children
     * @return bool
     */
    protected function each(array $children): bool
    {
        if (empty($children)) {
            return false;
        }
        // 循环下单
        foreach ($children as $child) {
            // 订单号
            $tradeNo = $this->bean->getOrderNo() . '-' . ($this->logic->countCycleByOrderNum($this->order->order_sn) + 1);
            try {
                // 电信过滤
                $this->telecomFiltering($child);
                // 获取api
                $api = $this->api($child, $this->supplier);
                // 检测配置
                $config = $this->cg->supplierApiConfigCheck($api);
                // 价格筛选
                $this->goodLogic->goodPriceToFilter($this->bean->getGood()->price, $child, $api, $config);
                // 生成bean
                $bean = $this->cg->makeBasicInformation($api, $this->order, $this->bean->getGood(), $config, $child)->setOrderNo($tradeNo);
                // 百分比单量筛选
                $this->percentageFiltering($child);
                // 请求
                if ($this->request($api, $child, $bean)){
                    return true;
                }
            } catch (SuspensionOfEnforcement $e) {
                $this->generateOrders($this->generateCyclePunchChildBean($child, $e->getMessage(), $e->peak_failure), $tradeNo);
            }
        }
        return false;
    }

    /**
     * 请求
     *
     * @param InterfaceConfiguration $config 第三方配置
     * @param array $child 子商品
     * @param BasicInformation $dataBean dataBean
     * @throws SuspensionOfEnforcement
     * @return bool
     */
    protected function request(InterfaceConfiguration $config, array $child, BasicInformation $dataBean): bool
    {
        // childBean
        $baseBean = $this->generateCyclePunchChildBean($child);
        // 获取api
        $interface = $this->obtainApiInterface($config);
        // 下单
        $resultBean = Library::executeRecharge($interface, httpClient(), $config, $dataBean);
        // 存进baseBean
        $baseBean->setResponse($resultBean);
        $baseBean->setResult($resultBean->isResult());
        $baseBean->setErrMessage($resultBean->getErrorMessage());
        // 保存循环订单
        $append = [];
        // 保存第三方订单号
        if ($config->trade_no_key) {
            $append['other_order'] = data_get($resultBean->getFormatResponse(), $config->trade_no_key, '');
        }
        $this->generateOrders($baseBean, $dataBean->getOrderNo(), $append);
        // 返回
        return $baseBean->isResult();
    }

    /**
     * 百分比筛选
     *
     * @param array $child 子商品
     * @throws SuspensionOfEnforcement
     */
    protected function percentageFiltering(array $child): void
    {
        if (isset($child['peak_failure']) && ($child['peak_failure'] == 1)) {
            return;
        }
        $percentage = max(intval($child['r_percentage'] ?? 0), 0);
        if ($percentage == 0) {
            return;
        }
        $good = $this->bean->getGood();
        // 获取最后一个uuid
        $uuRow = $this->logic->percentageLastUuid($good->id,  $this->bean->getPercentageSingular());
        // 设施uuid
        $this->bean->setPercentageUuid($uuRow['uuid']);
        // 获取当前uuid下已经走了几单了这个子商品
        $supplierStatusNum = $this->logic->percentageChildCount($uuRow['uuid'], $good->id, $child['id']);
        // 计算间隔单数
        $num = $this->bean->getPercentageSingular();
        // 转换一下
        $canBeOrderNum = (int)floor($percentage / 100 * $num);
        // 判断 是否可以走当前单
        if (($supplierStatusNum + 1) <= $canBeOrderNum) {
            // 可以的话加进去
            $this->logic->pushPercentage($uuRow['uuid'], $good->id, $child['id'], $this->bean->getOrderNo());
            // 返回
            return;
        }
        // 失败原因
        $msg = sprintf("单量已达到峰值,百分比为[%d],已经下单单量[%d]", $percentage, $supplierStatusNum);
        // 返回
        throw new SuspensionOfEnforcement($msg, true);
    }

    /**
     * 生成循环子商品bean
     *
     * @param array $child 子商品
     * @param string $errorMessage 错误信息
     * @param bool $peak_failure 是否是峰值失败
     * @return CyclePunchChildBean
     */
    protected function generateCyclePunchChildBean(array $child, string $errorMessage = "", bool $peak_failure = false): CyclePunchChildBean
    {
        $cb = new CyclePunchChildBean();
        $cb->setChild($child);
        $cb->setErrMessage($errorMessage);
        $cb->setPeakFailure($peak_failure);
        return $cb;
    }

    /**
     * 保存循环订单
     *
     * @param CyclePunchChildBean $cb
     * @param string $tradeNo
     * @param array $append
     * @return void
     */
    function generateOrders(CyclePunchChildBean $cb, string $tradeNo, array $append = []): void
    {
        try {
            $child = $cb->getChild();
            $data = [
                'order_no'          => $this->order->order_sn,
                'trade_no'          => $tradeNo,
                'gid'               => $child['id'],
                'good_name'         => $child['goods_name'],
                'good_code'         => $child['goods_code'],
                'unitPrice'         => $child['unit_price'],
                'state'             => $cb->isResult() ? 0 : 2,
                'supplier_status'   => $child['goods_belong'],
                'error_msg'         => $cb->getErrMessage(),
                'error_time'        => $cb->isResult() ? 0 : date('Y-m-d H:i:s'),
                'recharge_num'      => $this->order->goods_num,
                'pid'               => $this->bean->getGood()->id,
                'peak_failure'      => $cb->isPeakFailure() ? 1 : 2,
            ];
            if (!empty($append)) {
                $data = array_merge($data, $append);
            }
            // 查询
            $order = $this->logic->getCycleOrderByGid($data['order_no'],$this->bean->getGood()->id);
            // 如果存在 并且不是百分比失败的
            if ($order && $order->peak_failure == 2) {
                $data['error_msg'] = $order['error_msg'] == '' ? $data['error_msg'] : ($order['error_msg'.'|'.$data['error_msg']]);
                $order->update($data);
            } else {
                $data['create_time'] = time();
                CycleOrder::s($this->supplier)->create($data);
            }
        } catch (Throwable $e) {
            $this->log()->error('保存第三方下单失败:' . $e->getMessage(), [
                'ding'  => true,
                'bean'  => $this->bean->toArray(),
                'data'  => $cb->toArray(),
                'trace' => error2array($e)
            ]);
        }
    }

    /**
     * @return LoggerInterface
     */
    protected function log(): LoggerInterface
    {
        return Log::channel('cycle');
    }
}
